// FiveWin Text editor with color highlighting Class

#include "FiveWin.ch"
#include "Set.ch"

#define VK_BACKSPACE   8
#define VK_C          67

#define COLOR_WINDOW   5

#ifdef __XPP__
   #xtranslate Super => ::TControl
   #define New _New
#endif

//-------------------------------------------------------------------------//

CLASS TTxtEdit FROM TControl

   DATA   aLines          // Array with the text lines
   DATA   aDelLines       // Array where store the del lines
   DATA   aRDelLines      // Array where store the Undo lines
   DATA   nDelLines       // Max. number of deleted lines to store
   DATA   nVisLines       // Number of lines that fit in screen
   DATA   nLineTop        // First line visible on screen
   DATA   lInsert, lChanged
   DATA   cFileName, cOperators, cSeparators
   DATA   nRow, nCol      // Phisical row and col
   DATA   nLineRow, nLineCol  // Logical row and col
   DATA   cTokens1, cTokens2, cTokens3, cBegWith
   DATA   nClrTok1, nClrTok2, nClrTok3, nClrBegin
   DATA   nClrNumber, nClrString, nClrOperator

   DATA   nClrBBlock AS NUMERIC INIT CLR_BLUE
   DATA   nClrFBlock AS NUMERIC INIT CLR_WHITE
   DATA   nBlkStartRow, nBlkStartCol, nBlkEndRow, nBlkEndCol // Block markers
   DATA   lMPressed      // Mouse is pressed

   DATA   nTabLen
   DATA   nLeftMargin
   DATA   lVScroll  // Always show the vertical scrollbar
   DATA   lBackUp   // automatic .BAK files
   DATA   lUndo, lRedo
   DATA   nOldLine

   CLASSDATA lRegistered AS LOGICAL

   METHOD New( nTop, nLeft, nWidth, nHeight, oWnd ) CONSTRUCTOR
   METHOD Redefine( nId, oWnd ) CONSTRUCTOR

   METHOD AddLine( cLine )
   METHOD AddUndo()

   METHOD Change() INLINE ;
                   If( ::bChange != nil, Eval( ::bChange ),)

   METHOD CleanBlock() // Clean selected block

   METHOD Copy()
   METHOD Cut()      INLINE ::Copy(), ::DelBlock()

   METHOD Default()

   METHOD DelBlock()

   METHOD DelCurLine() INLINE ::DelLine( ::nLine() )

   MESSAGE Delete METHOD _Delete()      // response to Del Key

   METHOD DelLine( nLine )

   METHOD DlgGoLine()

   METHOD DrawLine( nLine, nRow, nClrBack )

   METHOD DrawCurLine() INLINE HideCaret( ::hWnd ),;
                        ::DrawLine( ::nLine(),;
                        ::nLineRow + 1, ::nClrPane ),;
                        ShowCaret( ::hWnd )

   METHOD Display() INLINE ::BeginPaint(), ::Paint(), ::EndPaint(), 0
   METHOD DupLine()

   METHOD GetDlgCode( nLastKey ) INLINE DLGC_WANTALLKEYS

   METHOD GetCol() INLINE ::nLineCol

   METHOD GetRow() INLINE ::nLineRow

   METHOD GoUp()
   METHOD GoDown()

   METHOD Initiate( hDlg )

   METHOD InsLine( nLine, cTxtIns )

   METHOD Load( cTxtFile )

   METHOD nGetTokenColor( cToken )

   METHOD nLine INLINE ::nLineTop + ::nLineRow

   METHOD _nLine( nNewLine ) INLINE ::GoLine( nNewLine )

   METHOD nLines INLINE Len( ::aLines )

   METHOD Paint()

   METHOD SelFile( cMask, cTitle )

   METHOD GotFocus()
   METHOD LostFocus() INLINE DestroyCaret(), Super:LostFocus()

   METHOD KeyChar( nKey, nFlags )

   METHOD KeyDown( nKey, nFlags )

   METHOD LButtonDown( nRow, nCol, nFlags )
   METHOD MouseMove( nRow, nCol, nKeyFlags )
   METHOD LButtonUp( nRow, nCol, nFlags )

   METHOD EraseBkGnd( hDC ) INLINE 1

   METHOD GoPrevLine()
   METHOD GoNextLine()
   METHOD GoPrevChar()
   METHOD GoNextChar()

   METHOD GoBeginLine()
   METHOD GoEndLine()

   METHOD GoPrevWord()
   METHOD GoNextWord()

   METHOD ToggleInsert()

   METHOD ReSize( nType, nWidth, nHeight )

   METHOD Search( cText, nFromLine )

   METHOD SelectAll()

   METHOD SetFont( oFont ) INLINE Super:SetFont( oFont ),;
              ::nVisLines = Int( ::nHeight() / ::nGetChrHeight() )

   METHOD SetText( cText )
   METHOD GetText()

   METHOD VScrAdjust()       // Vertical ScrollBar Range Adjust

   METHOD GoBottom()
   METHOD GoTop()
   METHOD GoLine( nLine )
   METHOD GoTo( nLine ) INLINE ::GoLine( nLine )

   METHOD PageUp()
   METHOD PageDown()

   METHOD Paste()
   METHOD JoinLine()
   METHOD SplitLine()
   METHOD SaveToFile()
   METHOD Redo()
   METHOD Undo()

ENDCLASS

//-------------------------------------------------------------------------//

METHOD New( nTop, nLeft, nWidth, nHeight, oWnd ) CLASS TTxtEdit                 // Method constructor

   #ifdef __XPP__
      #undef New
   #endif

   ::nTop      = nTop
   ::nLeft     = nLeft
   ::nBottom   = nTop + nHeight - 1
   ::nRight    = nLeft + nWidth - 1
   ::oWnd      = oWnd
   ::nStyle    = nOr( WS_CHILD, WS_VISIBLE, WS_BORDER, WS_VSCROLL )
   ::lVScroll  = .t.
   ::lBackUp   = .t.
   ::lUnDo     = .t.
   ::lReDo     = .t.
   ::lDrag     = .f.
   ::nLeftMargin  = 15
   ::nBlkStartRow = 0
   ::nBlkStartCol = 0
   ::nBlkEndRow   = 0
   ::nBlkEndCol   = 0
   ::lMPressed    = .F.
   ::Default()

   ::SetColor( ::nClrText, ::nClrPane )

   #ifdef __XPP__
      DEFAULT ::lRegistered := .f.
   #endif

   ::Register( nOR( CS_VREDRAW, CS_HREDRAW ) )

   if ! Empty( oWnd:hWnd )
      ::Create()
      ::lVisible = .t.
      ::nGetChrHeight()
      ::nVisLines = Int( nHeight / ::nChrHeight )
      oWnd:AddControl( Self )

      DEFINE SCROLLBAR ::oVScroll VERTICAL OF Self ;
         RANGE 1, 1 ;
         ON UP       ::GoPrevLine() ;
         ON DOWN     ::GoNextLine() ;
         ON PAGEUP   ::PageUp() ;
         ON PAGEDOWN ::PageDown() ;
         ON THUMBPOS ::GoLine( nPos )
      ::oVScroll:SetPage( Min( ::nVisLines, Len( ::aLines ) ) - 1 )
   else
      oWnd:DefControl( Self )
   endif

   ::bMouseWheel = { | nKey, nDelta, nXPos, nYPos | If( nDelta > 0, ::GoUp(), ::GoDown() ) }

return Self

//-------------------------------------------------------------------------//

METHOD Redefine( nId, oWnd ) CLASS TTxtEdit

   ::nId      = nId
   ::oWnd     = oWnd
   ::lVScroll = .t.
   ::lBackUp  = .t.
   ::lUnDo    = .t.
   ::lReDo    = .t.
   ::lDrag    = .f.
   ::nBlkStartRow = 0
   ::nBlkStartCol = 0
   ::nBlkEndRow   = 0
   ::nBlkEndCol   = 0
   ::nLeftMargin  = 15
   ::lMPressed    = .F.

   ::Default()

   #ifdef __XPP__
      DEFAULT ::lRegistered := .f.
   #endif

   ::Register( nOR( CS_VREDRAW, CS_HREDRAW ) )

   if oWnd != nil
      oWnd:DefControl( Self )
   endif

return Self

//-------------------------------------------------------------------------//

METHOD AddLine( cLine ) CLASS TTxtEdit

   DEFAULT cLine := ""

   AAdd( ::aLines, cLine )

return nil

//-------------------------------------------------------------------------//

METHOD Default() CLASS TTxtEdit

   ::aLines       = {}
   ::aDelLines    = {}
   ::aRDelLines   = {}
   ::nDelLines    = 40
   ::lInsert      = .t.
   ::lChanged     = .f.
   ::cFileName    = ""
   ::nRow         = 0
   ::nCol         = 0
   ::nLineRow     = 0
   ::nLineCol     = 0
   ::nLineTop     = 1
   ::cSeparators  = " ,;<>(){}@.:=[]/'+-|" + '"' + Chr( 13 ) + Chr( VK_TAB )
   ::cOperators   = "::+-/*%,;<>(){}@.:=![]|"
   ::nTabLen      = 3
   ::nClrPane     = GetSysColor( COLOR_WINDOW )
   ::nClrTok1     = CLR_HGREEN
   ::nClrTok2     = CLR_HCYAN
   ::nClrTok3     = CLR_YELLOW
   ::nClrBegin    = CLR_HRED
   ::nClrNumber   = CLR_YELLOW
   ::nClrString   = CLR_HBLUE
   ::nClrOperator = CLR_WHITE
   ::cTokens1     = " #INCLUDE #DEFINE FUNCTION RETURN CLASS CLASSDATA FROM" + ;
                    " DATA METHOD ENDCLASS DEFINE AS" + ;
                    " WINDOW ACTIVATE OBJECT ENDOBJECT ARRAY ENDARRAY BUTTON ACTION "
   ::cTokens2     = " LOCAL STATIC PUBLIC PRIVATE CONSTRUCTOR WHILE DO END ENDDO FOR NEXT STEP TO EXIT LOOP IF ELSE ELSEIF ENDIF SUPER SELF "
   ::cTokens3     = " NIL "
   ::cBegWith     = " // "

return nil

//-------------------------------------------------------------------------//

METHOD Initiate( hDlg ) CLASS TTxtEdit

   Super:Initiate( hDlg )

   ::lVisible = .t.
   ::nGetChrHeight()
   ::nVisLines = Int( ::nHeight() / ::nChrHeight )

   DEFINE SCROLLBAR ::oVScroll VERTICAL OF Self ;
      RANGE 1, 1 ;
      ON UP       ::GoPrevLine() ;
      ON DOWN     ::GoNextLine() ;
      ON PAGEUP   ::PageUp() ;
      ON PAGEDOWN ::PageDown() ;
      ON THUMBPOS ::GoLine( nPos )

return nil

//-------------------------------------------------------------------------//

METHOD DelBlock() CLASS TTxtEdit

   local nStart := Min( ::nBlkStartRow, ::nBlkEndRow )
   local nLines := Max( ::nBlkStartRow, ::nBlkEndRow ) - nStart + 1
   local n

   // ::AddUndo( ::nBlkStart, "D", nBlkLen )

   for n = 1 to nLines
      ADel( ::aLines, nStart )
      ASize( ::aLines, Len( ::aLines ) - 1 )
      ::GoPrevLine()
   next

   ::GoNextLine()

   ::nBlkStartRow = 0
   ::nBlkStartCol = 0
   ::nBlkEndRow   = 0
   ::nBlkEndCol   = 0

   ::Refresh()
   ::VScrAdjust()

   ::lChanged = .T.

return nil

//-------------------------------------------------------------------------//

METHOD _Delete() CLASS TTxtEdit

   if ::nBlkEndRow != 0
      ::DelBlock()
   else
      if ::nLineCol < Len( ::aLines[ ::nLine() ] )
         ::aLines[ ::nLine() ] = ;
         SubStr( ::aLines[ ::nLine() ], 1, ::nLineCol ) + ;
         SubStr( ::aLines[ ::nLine() ], ::nLineCol + 2 )
         ::DrawCurLine()
      else
         if ::nLine() < Len( ::aLines )
            ::JoinLine()
         endif
      endif
   endif

   ::lChanged = .t.

return nil

//-------------------------------------------------------------------------//

METHOD DelLine( nLine ) CLASS TTxtEdit

   if nLine < Len( ::aLines )
      ::AddUndo( nLine, "D" )
      ADel( ::aLines, nLine )
      ASize( ::aLines, Len( ::aLines ) - 1 )
      ::VScrAdjust()
   endif
   ::Refresh()
   ::lChanged = .t.

return nil

//-------------------------------------------------------------------------//

METHOD DrawLine( nLine, nRow, nClrBack ) CLASS TTxtEdit

   local cWord
   local nOffset := 0, nAt
   local cTextLine
   local nCol := 0
   local nChrHeight
   local lString1 := .F., lString2 := .F.
   local lKeepColor := .F.

   if nLine <= 0 .or. nLine > Len( ::aLines )
      return nil
   endif

   cTextLine := ::aLines[ nLine ]

   DEFAULT ::nChrHeight := ::nGetChrHeight(),;
           nClrBack := ::nClrPane,;
           nRow := ::nRowPos

   nChrHeight = ::nChrHeight

   if nLine >= Min( ::nBlkStartRow, ::nBlkEndRow ) .and. nLine <= Max( ::nBlkStartRow, ::nBlkEndRow )
      SetBkColor( ::GetDC(), ::nClrBBlock )
   else
      SetBkColor( ::GetDC(), nClrBack )
   endif

   if ::oFont != nil
      SelectObject( ::hDC, ::oFont:hFont )
   endif

   while ! lKeepColor .and. nOffset < Len( cTextLine )
      cWord = cStrWord( cTextLine, @nOffset, ::cSeparators )

      if cWord == "/" .and. cStrWord( cTextLine, nOffset, ::cSeparators ) == "/"
         cWord = "//"
         nOffset++
      endif

      do case
         case cWord == '"' .and. ! lString2
              lString1 = ! lString1
              SetTextColor( ::hDC, ::nClrString )

         case cWord == "'" .and. ! lString1
              lString2 = ! lString2
              SetTextColor( ::hDC, ::nClrString )

         case lString1 .or. lString2
              SetTextColor( ::hDC, ::nClrString )

         case SubStr( LTrim( cWord ), 1, 1 ) >= "0" .and. ;
              SubStr( LTrim( cWord ), 1, 1 ) <= "9"
              SetTextColor( ::hDC, ::nClrNumber )

         case At( LTrim( cWord ), ::cOperators ) != 0
              SetTextColor( ::hDC, ::nClrOperator )

         case ( nAt := At( " " + Upper( LTrim( cWord ) ) + " ",;
                           ::cTokens1 ) ) != 0
              SetTextColor( ::hDC, ::nClrTok1 )

         case ( nAt := At( " " + Upper( LTrim( cWord ) ) + " ",;
                           ::cTokens2 ) ) != 0
              SetTextColor( ::hDC, ::nClrTok2 )

         case ( nAt := At( " " + Upper( LTrim( cWord ) ) + " ",;
                           ::cTokens3 ) ) != 0
              SetTextColor( ::hDC, ::nClrTok3 )

         case ( nAt := At( " " + Upper( LTrim( cWord ) ) + " ",;
                           ::cBegWith ) ) != 0
              SetTextColor( ::hDC, ::nClrBegin )
              lKeepColor = .t.

         otherwise
              if nLine >= Min( ::nBlkStartRow, ::nBlkEndRow ) .and. nLine <= Max( ::nBlkStartRow, ::nBlkEndRow )
                 SetTextColor( ::hDC, ::nClrFBlock )
              else
                 SetTextColor( ::hDC, ::nClrText )
              endif
      endcase

      TextOut( ::hDC, ( nRow - 1 ) * nChrHeight,;
               nCol + ::nLeftMargin, cWord )

      nCol += ::GetWidth( cWord )
   end

   if lKeepColor
      TextOut( ::hDC, ( nRow - 1 ) * nChrHeight, nCol + ::nLeftMargin,;
               cWord := SubStr( cTextLine, nOffset + 1 ) )
      nCol += ::GetWidth( cWord )
   endif

   TextOut( ::hDC, ( nRow - 1 ) * nChrHeight,;
            nCol + ::nLeftMargin, Space( 200 ) )

   ::ReleaseDC()

return nil

//----------------------------------------------------------------------------//

METHOD GoUp() CLASS TTxtEdit

   if ::nLine > 1
      if GetKeyState( VK_SHIFT )
         if ::nBlkStartRow == 0
            ::nBlkStartRow = ::nLine
            ::nBlkEndRow   = ::nLine
         else
            ::nBlkEndRow--
         endif
      endif
      ::GoPrevLine()
   endif

return nil

//----------------------------------------------------------------------------//

METHOD GoDown() CLASS TTxtEdit

   if ::nLine < Len( ::aLines ) + 1
      if GetKeyState( VK_SHIFT )
         if ::nBlkStartRow == 0
            ::nBlkStartRow = ::nLine
            ::nBlkEndRow   = ::nLine
         else
            ::nBlkEndRow++
         endif
      endif
      ::GoNextLine()
   endif

return nil

//----------------------------------------------------------------------------//

METHOD InsLine( nLine, cTxtIns ) CLASS TTxtEdit

   DEFAULT nLine := ::nLine(), ;
           cTxtIns := ""

   ::AddUndo( nLine, "I" )

   AAdd( ::aLines, nil )
   AIns( ::aLines, nLine )
   ::aLines[ nLine ] = cTxtIns
   ::VScrAdjust()
   ::Refresh()
   ::lChanged = .t.

return nil

//----------------------------------------------------------------------------//

METHOD Load( cTxtFile ) CLASS TTxtEdit      // Load a file from disk

   if Empty( cTxtFile )
      return nil
   endif

   if ! File( cTxtFile )
      MsgStop( "Can't find the file: " + cTxtFile )
      return nil
   endif

   ::SetText( MemoRead( cTxtFile ) )

return nil

//-------------------------------------------------------------------------//

METHOD Search( cText, nFromLine ) CLASS TTxtEdit

   local lFound := .f.

   DEFAULT nFromLine := 1

   while nFromLine < Len( ::aLines ) .and. ! lFound
      lFound = ( At( cText, ::aLines[ nFromLine ] ) != 0 )
      nFromLine++
   end

   if lFound
      nFromLine--
   endif

return If( lFound, nFromLine, 0 )

//-------------------------------------------------------------------------//

METHOD SelFile( cMask, cTitle ) CLASS TTxtEdit     // Interactively select
                                                   // a file to edit
   local cFile

   DEFAULT cMask := "*.*", cTitle := "Select a file to edit"

   if File( cFile := cGetFile( cMask, cTitle ) )
      ::cFileName = cFile
      ::Load( cFile )
   endif

return nil

//-------------------------------------------------------------------------//

METHOD GotFocus() CLASS TTxtEdit

   if Set( _SET_INSERT )
      CreateCaret( ::hWnd, 0, 6, ::nGetChrHeight() )
   else
      CreateCaret( ::hWnd, 0, 2, ::nGetChrHeight() )
   endif
   ShowCaret( ::hWnd )
   SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )

return Super:GotFocus()

//-------------------------------------------------------------------------//

METHOD KeyChar( nKey, nFlags ) CLASS TTxtEdit

   local cLastToken, nColor

   if Len( ::aLines ) == 0
      ::aLines = { "" }
      ::Refresh()
   endif

   do case
      //------------------------------------------------------------------------------
      // by Alexandre inicio
      case GetKeyState( VK_CONTROL ) .and. nKey == VK_C
         // Control S
         IF nKey == 19
            ::SaveToFile()
         // Control D
         ELSEIF nKey == 4
            ::DupLine()
         // Control C
         ELSEIF nKey == 3
            ::Copy()
         // Control X
         ELSEIF nKey == 24
            ::Cut()
         // Control Y
         ELSEIF nKey == 25
            ::DelLine(::nLine())
         // Control V
         ELSEIF nKey == 22
            ::Paste()
         // Control Z
         ELSEIF nKey == 26
            ::Undo()
         END
      // by Alexandre fim
      //------------------------------------------------------------------------------
      //case GetKeyState( VK_CONTROL ) .and. nKey == VK_C
      //     if ::nBlkEndRow != 0
      //        ::Copy()
      //     endif

      case nKey == VK_BACKSPACE
           if ::nLineCol > 0
              ::aLines[ ::nLine() ] = ;
                 SubStr( ::aLines[ ::nLine() ], 1, ::nLineCol - 1 ) + ;
                 SubStr( ::aLines[ ::nLine() ], ::nLineCol + 1 )
              ::DrawCurLine()
              ::GoPrevChar()
           else
              if ::nLine() > 1
                 ::GoPrevChar()
                 ::JoinLine()
              endif
           endif

      case nKey == VK_RETURN
           if ! Set( _SET_INSERT )
              if ::nLineCol = Len( ::aLines )
                 ::InsLine()
              else
                 ::SplitLine()
              endif
           else
              ::nRow += ::nChrHeight
              ::nCol  =  0
              ::nLineRow++
              ::nLineCol = 0
              if Len( ::aLines ) < ::nLine()
                 AAdd( ::aLines, "" )
                 ::VScrAdjust()
                 ::Refresh()
              endif
              SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
           endif
           ::oVScroll:GoDown()

      case nKey == VK_TAB
           ::nLineCol += ::nTabLen
           HideCaret( ::hWnd )
           ::Say( ::nRow, ::nCol + ::nLeftMargin, Space( ::nTabLen ),,,,.t. )
           ::aLines[ ::nLine() ] += Chr( VK_TAB )
           ::nCol += ::GetWidth( Space( ::nTabLen ) )
           SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
           ShowCaret( ::hWnd )

      case nKey == VK_ESCAPE

      otherwise
          HideCaret( ::hWnd )
          if ::nLineCol <= Len( ::aLines[ ::nLine() ] )
             if ! Set( _SET_INSERT )
                ::aLines[ ::nLine() ] = ;
                   SubStr( ::aLines[ ::nLine() ], 1,;
                   ::nLineCol ) + Chr( nKey ) + SubStr( ;
                   ::aLines[ ::nLine() ], ::nLineCol + 1 )
             else
                ::aLines[ ::nLine() ] = ;
                   SubStr( ::aLines[ ::nLine() ], 1,;
                   ::nLineCol ) + Chr( nKey ) + SubStr( ;
                   ::aLines[ ::nLine() ], ::nLineCol + 2 )
             endif
             ::DrawCurLine()
          else
             ::Say( ::nRow, ::nCol + ::nLeftMargin, Chr( nKey ),,,,.t. )
             ::aLines[ ::nLine() ] += Chr( nKey )
          endif
          ::nCol += ::GetWidth( Chr( nKey ) )
          SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
          ShowCaret( ::hWnd )
          ::nLineCol++
   endcase

   ::lChanged = .t.

return nil

//-------------------------------------------------------------------------//

METHOD KeyDown( nKey, nFlags ) CLASS TTxtEdit

   do case
      case nKey == VK_UP
           if ::nLine() > 1
              if GetKeyState( VK_SHIFT )
                 if ::nBlkEndRow == 0
                    ::nBlkStartRow = ::nLine()
                 endif
                 ::nBlkEndRow = ::nLine() - 1
                 ::Refresh()
              else
                 if ::nBlkEndRow != 0
                    ::CleanBlock()
                 endif
              endif
              ::GoPrevLine()
           endif

      case nKey == VK_DOWN
           if ::nLine() < Len( ::aLines )
              if GetKeyState( VK_SHIFT )
                 if ::nBlkEndRow == 0
                    ::nBlkStartRow = ::nLine()
                 endif
                 ::nBlkEndRow = ::nLine() + 1
                 ::Refresh()
              else
                 if ::nBlkEndRow != 0
                    ::CleanBlock()
                 endif
              endif
              ::GoNextLine()
           endif

      case nKey == VK_RIGHT
           if GetKeyState( VK_CONTROL )
              ::GoNextWord()
           else
              ::GoNextChar()
           endif
           if ::nBlkEndRow != 0 .and. ::nLine() > ::nBlkEndRow
              ::nBlkEndRow = ::nLine()
              ::Refresh()
           endif

      case nKey == VK_LEFT
           if GetKeyState( VK_CONTROL )
              ::GoPrevWord()
           else
              ::GoPrevChar()
           endif
           if ::nBlkEndRow != 0 .and. ::nLine() < ::nBlkEndRow
              ::nBlkEndRow = ::nLine()
              ::Refresh()
           endif

      case nKey == VK_HOME
           ::GoBeginLine()

      case nKey == VK_END
           ::GoEndLine()

      case nKey == VK_DELETE
           ::Delete()

      case nKey == VK_INSERT
           do case
              case GetKeyState( VK_CONTROL )
                   ::Copy()

              case GetKeyState( VK_SHIFT )
                   ::Paste()

              otherwise
                   ::ToggleInsert()
           endcase

      case nKey == VK_NEXT
           if GetKeyState( VK_CONTROL )
              ::GoBottom()
           else
              ::PageDown()
           endif

      case nKey == VK_PRIOR
           if GetKeyState( VK_CONTROL )
              ::GoTop()
           else
              ::PageUp()
           endif

      case nKey == VK_F4
           ::DupLine()
   endcase

   ::Change()

return nil

//-------------------------------------------------------------------------//

METHOD nGetTokenColor( cToken ) CLASS TTxtEdit

   local nColor := 0

   cToken = Upper( AllTrim( cToken ) )

   do case
      case SubStr( cToken, 1, 1 ) == '"'
           nColor = ::nClrString

      case SubStr( cToken, 1, 1 ) >= "0" .and. ;
           SubStr( cToken, 1, 1 ) <= "9"
           nColor = ::nClrNumber

      case At( " " + cToken + " ", ::cTokens1 ) != 0
           nColor = ::nClrTok1

      case At( " " + cToken + " ", ::cTokens2 ) != 0
           nColor = ::nClrTok2

      case At( " " + cToken + " ", ::cTokens3 ) != 0
           nColor = ::nClrTok3

      case At( " " + cToken + " ", ::cBegWith ) != 0
           nColor = ::nClrBegin
   endcase

return nColor

//-------------------------------------------------------------------------//

METHOD Paint() CLASS TTxtEdit

   local n, hOldFont, aInfo := ::DispBegin()

   HideCaret( ::hWnd )

   FillRect( ::hDC, GetClientRect( ::hWnd ), ::oBrush:hBrush )

   for n = 1 to Min( Len( ::aLines ) - ::nLineTop + 1, ::nVisLines )
      ::DrawLine( ::nLineTop + n - 1, n, ::nClrPane )
   next
   if n < ::nVisLines
      if Len( ::aLines ) == 0 .and. ::oFont != nil
         hOldFont = SelectObject( ::hDC, ::oFont:hFont )
      endif
      SetTextColor( ::hDC, CLR_BLACK )
      SetBkColor( ::hDC, CLR_WHITE )
      TextOut( ::hDC, ::nChrHeight * ( n - 1 ), ::nLeftMargin,;
               PadR( ">> EOF <<", 200 ) )
      if hOldFont != nil
         SelectObject( ::hDC, hOldFont )
      endif
   endif
   ShowCaret( ::hWnd )

   ::DispEnd( aInfo )

return nil

//-------------------------------------------------------------------------//

METHOD GoPrevLine() CLASS TTxtEdit

   if ::nLineRow == 0
      if ::nLineTop > 1
         HideCaret( ::hWnd )
         WScroll( ::hWnd, -1, ::oFont:hFont )
         ::DrawLine( --::nLineTop + ::nLineRow, 1, ::nClrPane )
         SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
         ShowCaret( ::hWnd )
         ::oVScroll:SetPos( ::nLine() )
         ::Change()
      else
         MsgBeep()
      endif
   else
      ::nRow -= ::nChrHeight
      ::nLineRow--
      ::nLineCol = Min( Len( ::aLines[ ::nLine() ] ), ::nLineCol )
      ::nCol = ::GetWidth( SubStr( ::aLines[ ::nLine() ], 1, ::nLineCol ) )
      HideCaret( ::hWnd )
      SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
      ShowCaret( ::hWnd )
      ::oVScroll:SetPos( ::nLine() )
      ::Change()
   endif

return nil

//-------------------------------------------------------------------------//

METHOD GoNextLine() CLASS TTxtEdit

   if ::nLineTop + ::nLineRow < Len( ::aLines )
      if ::nLineRow = ::nVisLines - 1
         HideCaret( ::hWnd )
         WScroll( ::hWnd, 1, ::oFont:hFont )
         ::DrawLine( ++::nLineTop + ::nLineRow, ::nVisLines, ::nClrPane )
         SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
         ShowCaret( ::hWnd )
      else
         ::nRow += ::nChrHeight
         ::nLineRow++
         ::nLineCol = Min( Len( ::aLines[ ::nLine() ] ), ::nLineCol )
         ::nCol = ::GetWidth( SubStr( ::aLines[ ::nLine() ], 1, ::nLineCol ) )
         SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
      endif
      ::oVScroll:SetPos( ::nLine() )
      ::Change()
   endif

return nil

//-------------------------------------------------------------------------//

METHOD GoNextChar() CLASS TTxtEdit

   local cChar

   if ::nLineCol < Len( ::aLines[ ::nLine() ] )
      cChar = StrChar( ::aLines[ ::nLine() ], ::nLineCol++ + 1 )
      if cChar == Chr( VK_TAB )
         ::nCol += ::GetWidth( Space( ::nTabLen ) )
      else
         ::nCol += ::GetWidth( cChar )
      endif
      SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
   else
      if ::nLine() < Len( ::aLines )
         ::nLineRow++
         ::nLineCol = 0
         ::nRow    += ::nChrHeight
         ::nCol     = 0
         SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
         ::oVScroll:SetPos( ::oVScroll:GetPos() + 1 )
      endif
   endif

return nil

//-------------------------------------------------------------------------//

METHOD GoPrevChar() CLASS TTxtEdit

   local cChar

   if ::nLineCol > 0
      cChar = StrChar( ::aLines[ ::nLineTop + ::nLineRow ], --::nLineCol + 1 )
      if cChar == Chr( VK_TAB )
         ::nCol -= ::GetWidth( Space( ::nTabLen ) )
      else
         ::nCol -= ::GetWidth( cChar )
      endif
      SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
   else
      if ::nLineRow > 0
         ::nLineRow--
         ::nLineCol = Len( ::aLines[ ::nLineTop + ::nLineRow ] )
         ::nRow -= ::nChrHeight
         ::nCol  = ::GetWidth( ::aLines[ ::nLineTop + ::nLineRow ] )
         SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
         ::oVScroll:SetPos( ::oVScroll:GetPos() - 1 )
      endif
   endif

return nil

//-------------------------------------------------------------------------//

METHOD GoBeginLine() CLASS TTxtEdit

   if ::nLineCol > 0
      ::nLineCol = 0
      ::nCol     = 0
      SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
   endif

return nil

//-------------------------------------------------------------------------//

METHOD GoEndLine() CLASS TTxtEdit

   local cLine := StrTran( ::aLines[ ::nLine() ],;
                  Chr( VK_TAB ), Space( ::nTabLen ) )

   if ::nLineCol < Len( ::aLines[ ::nLine() ] )
      ::nLineCol = Len( ::aLines[ ::nLine() ] )
      ::nCol = ::GetWidth( cLine )
      SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
  endif

return nil

//-------------------------------------------------------------------------//

METHOD GoNextWord() CLASS TTxtEdit

   local n := ::nLineCol
   local cLine := StrTran( ::aLines[ ::nLine() ],;
                           Chr( VK_TAB ), Space( ::nTabLen ) )

   while ! StrChar( cLine, n + 2 ) $ ::cSeparators .and. ;
         n++ < Len( cLine )
   end

   if n < Len( cLine )
      while StrChar( cLine, n + 2 ) $ ::cSeparators .and. ;
            n++ < Len( cLine )
      end
      if ! StrChar( cLine, n + 2 ) $ ::cSeparators
         ::nLineCol = n + 1
         ::nCol = ::GetWidth( SubStr( cLine, 1, ::nLineCol ) )
         SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
      endif
  endif

return nil

//-------------------------------------------------------------------------//

METHOD GoPrevWord() CLASS TTxtEdit

   local n := ::nLineCol

   while StrChar( ::aLines[ ::nLine() ], n ) == " " .and. ;
         --n > 0
   end

   if n > 0
      while StrChar( ::aLines[ ::nLine() ], n ) != " " .and. ;
            --n > 0
      end
      ::nLineCol = n
      ::nCol = ::GetWidth( SubStr( ::aLines[ ::nLine() ], 1,;
                           ::nLineCol ) )
      SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
   endif

return nil

//-------------------------------------------------------------------------//

METHOD ToggleInsert() CLASS TTxtEdit

   Set( _SET_INSERT, ! Set( _SET_INSERT ) )
   DestroyCaret()
   if Set( _SET_INSERT )
      CreateCaret( ::hWnd, 0, 6, ::nGetChrHeight() )
   else
      CreateCaret( ::hWnd, 0, 2, ::nGetChrHeight() )
   endif
   ShowCaret( ::hWnd )

return nil

//-------------------------------------------------------------------------//

METHOD LButtonDown( nRow, nCol, nFlags ) CLASS TTxtEdit

   local nLine   := Int( nRow / ::nChrHeight )
   local n := 0, nColPos := 0, nChrWidth := 1
   local cLine

   if GetFocus() != ::hWnd
      SetFocus( ::hWnd )
   endif

   nCol -= ::nLeftMargin

   if Empty( ::aLines ) .or. nLine > Len( ::aLines ) - 1
      return nil
   endif

   ::lMPressed = .T.

   if ::nBlkEndRow != 0
      if ( nLine + 1 ) < Min( ::nBlkStartRow, ::nBlkEndRow ) .or. ;
         ( nLine + 1 ) > Max( ::nBlkStartRow, ::nBlkEndRow )
         ::CleanBlock()
      endif
   endif

   if ::nLineRow != nLine
      ::nLineRow = nLine
      ::nRow     = nLine * ::nChrHeight
      ::oVScroll:SetPos( ::nLine() )
   endif

   cLine = ::aLines[ Min( ::nLine(), Len( ::aLines ) ) ]
   while nColPos < nCol .and. n < Len( cLine ) - 1
      nChrWidth = ::GetWidth( StrChar( cLine, n++ + 1 ) )
      nColPos  += nChrWidth
   end
   if ( nColPos - nCol ) > ( nChrWidth / 2 )
      ::nCol     = nColPos - nChrWidth
      ::nLineCol = --n
   else
      ::nCol     = nColPos
      ::nLineCol = n
   endif
   SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
   ::nOldLine = ::nLine()
   ::Change()

return nil

//-------------------------------------------------------------------------//

METHOD LButtonUp( nRow, nCol, nFlags ) CLASS TTxtEdit

   ::lMPressed = .F.

return nil

//-------------------------------------------------------------------------//

METHOD ReSize( nType, nWidth, nHeight ) CLASS TTxtEdit

   DEFAULT nHeight := ::nHeight()

   ::nVisLines = Int( nHeight / ::nChrHeight )
   ::VScrAdjust()

return Super:ReSize( nType, nWidth, nHeight )

//-------------------------------------------------------------------------//

METHOD SelectAll() CLASS TTxtEdit

   if Len( ::aLines ) > 0
      ::nBlkStartRow = 1
      ::nBlkEndRow   = Len( ::aLines )
      ::Refresh()
   endif

//-------------------------------------------------------------------------//

METHOD SetText( cText ) CLASS TTxtEdit

   local n, nFrom := 1, nLen := Len( cText ), cLine

   ::aLines   = {}
   ::nRow     = 0
   ::nCol     = 0
   ::nLineRow = 0
   ::nLineCol = 0

   while nFrom <= nLen
      cLine = ExtractLine( cText, @nFrom )
      AAdd( ::aLines, cLine )
   end

   ::VScrAdjust()
   ::lChanged = .F.
   ::Refresh()

return nil

//-------------------------------------------------------------------------//

METHOD GetText() CLASS TTxtEdit

   local cText := ""
   local n, nLen := len(::aLines)


   for n = 1 to nLen
      cText += ::aLines[n]+CRLF
   next

return cText

//-------------------------------------------------------------------------//

METHOD VScrAdjust() CLASS TTxtEdit

   if ::lVScroll
      if ::oVScroll != nil
         ::oVScroll:SetRange( 1, Len( ::aLines ) )
         ::oVScroll:SetPage( Min( ::nVisLines, Len( ::aLines ) ) - 1 )
      endif
   else
      if Len( ::aLines ) > ::nVisLines
         ::oVScroll:SetRange( 1, Len( ::aLines ) )
      else
         ::oVScroll:SetRange( 0, 0 )
      endif
   endif

return nil

//-------------------------------------------------------------------------//

METHOD DlgGoLine() CLASS TTxtEdit

   local oGLine, nLine := ::nLine()

   DEFINE DIALOG oGLine FROM 5,5 TO 10.2,29 TITLE "Go To"

   @ 0.5, 2 SAY "Line:" OF oGLine
   @ 0.5, 5 GET nLine OF oGLine PICTURE "99999" SIZE 25, 11

   @ 1.3, 1.4 BUTTON "&Ok"  OF oGLine SIZE 35, 11 ;
      ACTION  ( IF( nLine < ( Len( ::aLines ) + 1 ) .and. ( nLine > 0 ), ;
              ::GoLine( nLine ), ;
              ::GoBottom() ), ;
              SetCaretPos( ::nCol + ::nLeftMargin, ::nRow ), ::VScrAdjust(), ;
              ::Refresh(), oGLine:End() )

   @ 1.3, 8.9 BUTTON "&Cancel" OF oGLine SIZE 35, 11 ;
      ACTION oGLine:End()

   ACTIVATE DIALOG oGLine CENTERED

return nil

//-------------------------------------------------------------------------//

METHOD GoLine( nLine ) CLASS TTxtEdit

   ::nLineTop = Max( 1, nLine - ::nLineRow )

   ::Refresh()
   ::oVScroll:SetPos( nLine )
   ::Change()

return nil

//-------------------------------------------------------------------------//

METHOD GoBottom() CLASS TTxtEdit

   local n, nMaxLine := Min( ::nVisLines, Len( ::aLines ) )

   HideCaret( ::hWnd )
   ::nLineTop = Len( ::aLines ) - ( nMaxLine - 1 )

   for n = 1 to nMaxLine
      ::DrawLine( ::nLineTop + n - 1, n, ::nClrPane )
   next

   ::nLineRow = n - 2
   ::nCol = 1
   ::nRow = ( nMaxLine - 1 ) * ::nChrHeight
   SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
   ShowCaret( ::hWnd )
   ::oVScroll:SetPos( ::nRoW )
   ::Change()

return nil

//-------------------------------------------------------------------------//

METHOD GoTop() CLASS TTxtEdit

   local n

   HideCaret( ::hWnd )
   ::nLineTop = 1

   for n = 1 to Min( ::nVisLines, Len( ::aLines ) )
      ::DrawLine( ::nLineTop + n - 1, n, ::nClrPane )
   next

   ::nLineRow = 0
   ::nCol = 1
   ::nRow = 0
   SetCaretPos( ::nCol + ::nLeftMargin, ::nRow )
   ShowCaret( ::hWnd )
   ::oVScroll:SetPos( ::nRoW )
   ::Change()

return nil

//-------------------------------------------------------------------------//

METHOD CleanBlock() CLASS TTxtEdit

   ::nBlkStartRow = 0
   ::nBlkStartCol = 0
   ::nBlkEndRow   = 0
   ::nBlkEndCol   = 0
   ::Refresh()

return nil

//-------------------------------------------------------------------------//

METHOD Copy() CLASS TTxtEdit

   local cCopyTxt := "", nCopyLine
   local oClp

   DEFINE CLIPBOARD oClp OF Self

   if ::nBlkEndRow != 0
      nCopyLine = Min( ::nBlkStartRow, ::nBlkEndRow )
      while nCopyLine < ( Max( ::nBlkStartRow, ::nBlkEndRow ) + 1 )
         cCopyTxt = cCopyTxt + ::aLines[ nCopyLine ] + CRLF
         nCopyLine++
      end
      oClp:Clear()
      oClp:SetText( cCopyTxt )
   endif

return nil

//-------------------------------------------------------------------------//

METHOD Paste() CLASS TTxtEdit

   local nCountLine, cCopyTxt, oClp

   DEFINE CLIPBOARD oClp OF Self
   cCopyTxt = oClp:GetText()

   if ! Empty( cCopyTxt )
      HideCaret( ::hWnd )
      for nCountLine = MlCount( cCopyTxt ) to 1 step -1
          ::lUndo = .f.
          ::InsLine( ::nLine() , MemoLine( cCopyTxt,, nCountLine, .t. ) )
          ::lUndo = .t.
          ::AddUndo( ::nLine(), "I", MlCount( cCopyTxt ) )
      next
      ShowCaret( ::hWnd )
   endif

return nil

//-------------------------------------------------------------------------//

METHOD DupLine() CLASS TTxtEdit

   local cTxtInsert := ::aLines[ ::nLine() ]

   ::InsLine( ::nLine(), cTxtInsert )

return nil

//----------------------------------------------------------------------------//

METHOD JoinLine() CLASS TTxtEdit

   if ::nLine() > 0
      ::AddUndo( ::nLine(), "M" )
      ::AddUndo( ::nLine() + 1, "D" )
      ::aLines[ ::nLine() ] = ::aLines[ ::nLine() ] + ::aLines[ ::nLine() + 1 ]
      ADel( ::aLines, ::nLine() + 1 )
      ASize( ::aLines, Len( ::aLines ) - 1 )
      ::VScrAdjust()
      ::Refresh()
   endif
   ::lChanged = .t.

return nil

//----------------------------------------------------------------------------//

METHOD SplitLine() CLASS TTxtEdit

   local cNewtext := SubStr( ::aLines[ ::nLine() ], ::nLineCol + 1, ;
                 ( Len( ::aLines[ ::nLine() ] ) +  1 ) - ::nLineCol )

   ::AddUndo( ::nLine(), "M" )
   ::AddUndo( ::nLine() + 1, "I" )

   ::aLines[ ::nLine() ] = SubStr( ::aLines[ ::nLine() ], 1, ::nLineCol )
   ::DrawCurLine()
   ::InsLine( ::nLine() + 1, cNewtext )
   ::GoBeginLine()

return nil

//----------------------------------------------------------------------------//

METHOD SaveToFile( cTxtFile ) CLASS TTxtEdit

   local nIHdl, n

   DEFAULT ::lBackUp := .t., cTxtFile := ::cFileName

   if ::lBackUp .and. File( cTxtFile )
      FReName( cTxtFile, SubStr( cTxtFile, 1, RAt( ".", cTxtFile ) - 1 ) + ;
              ".BAK" )
   endif

   // if ::lChanged
      if ( nIHdl := FCreate( cTxtFile, 0 )) > 0
         for n = 1 to Len( ::aLines )
            FWrite( nIHdl, ::aLines[ n ] + CRLF )
         next
         FClose( nIHdl )
         ::lChanged = .f.
      else
         MsgInfo( "Can't create file " + cTxtFile )
      endif
   // endif

return nil

//----------------------------------------------------------------------------//

METHOD Undo() CLASS TTxtEdit

   local n, nCont, Line, cAccion, cString

   if Len( ::aDelLines ) > 0

      ::lUndo = .f.

      nCont   := Val( SubStr( ::aDelLines[ 1 ], 1, ;
                    At( "$", ::aDelLines[ 1 ] ) - 1 ) )

      for n = 1 to nCont
         Line    := Val( SubStr( ::aDelLines[ 1 ], ;
                       At( "$", ::aDelLines[ 1 ] ) + 1, ;
                       At( "#", ::aDelLines[ 1 ] ) - ;
                       ( At( "$", ::aDelLines[ 1 ] ) + 1 ) ) )
         cAccion   := SubStr( ::aDelLines[ 1 ], ;
                       At( "#", ::aDelLines[ 1 ] ) + 1, ;
                       At( "&", ::aDelLines[ 1 ] ) - ;
                       ( At( "#", ::aDelLines[ 1 ] ) + 1 ) )
         cString := SubStr( ::aDelLines[ 1 ], ;
                       At( "&", ::aDelLines[ 1 ] ) + 1, ;
                       Len( ::aDelLines[ 1 ] ) )

         ASize( ::aRDelLines, Len( ::aRDelLines ) + 1 )
         AIns( ::aRDelLines, 1 )
         ACopy( ::aDelLines, ::aRDelLines, 1, 1, 1 )

         ADel( ::aDelLines, 1 )
         ASize( ::aDelLines, Len( ::aDelLines ) - 1 )

         if( cAccion = "D", ::InsLine( Line, cString ), )
         if( cAccion = "M", ::aLines[ Line ] := cString, )
         if( cAccion = "I", ::DelLine( Line ), )
      next

      ::lUndo = .t.

   endif

return nil

//----------------------------------------------------------------------------//

METHOD Redo() CLASS TTxtEdit

   local n, nCont, Line, cAccion, cString

   if Len( ::aRDelLines ) > 0

      ::lUndo = .f.

      nCont   := Val( SubStr( ::aRDelLines[ 1 ], 1, ;
                    At( "$", ::aRDelLines[ 1 ] ) - 1 ) )
      for n = nCont to 1 step -1
         Line    := Val( SubStr( ::aRDelLines[ 1 ], ;
                       At( "$", ::aRDelLines[ 1 ] ) + 1, ;
                       At( "#", ::aRDelLines[ 1 ] ) - ;
                       ( At( "$", ::aRDelLines[ 1 ] ) + 1 ) ) )
         cAccion   := SubStr( ::aRDelLines[ 1 ], ;
                       At( "#", ::aRDelLines[ 1 ] ) + 1, ;
                       At( "&", ::aRDelLines[ 1 ] ) - ;
                       ( At( "#", ::aRDelLines[ 1 ] ) + 1 ) )
         cString := SubStr( ::aRDelLines[ 1 ], ;
                       At( "&", ::aRDelLines[ 1 ] ) + 1, ;
                       Len( ::aRDelLines[ 1 ] ) )

         ASize( ::aDelLines, Len( ::aDelLines ) + 1 )
         AIns( ::aDelLines, 1 )
         ACopy( ::aRDelLines, ::aDelLines, 1, 1, 1 )

         ADel( ::aRDelLines, 1 )
         ASize( ::aRDelLines, Len( ::aRDelLines ) - 1 )

         do case
            case  cAccion = "D"
               ::DelLine( Line )
            case  cAccion = "M"
               ::aLines[ Line ] = cString
            case  cAccion = "I"
               ::InsLine( Line, cString )
         endcase
      next

      ::lUndo = .t.

   endif

return nil

//----------------------------------------------------------------------------//

METHOD AddUndo( nLine, cOper, Cont )  CLASS TTxtEdit

   DEFAULT Cont := 1

   if ::lUndo .and. nLine <= len(::aLines)
      if Len( ::aDelLines ) < ::nDelLines
         AAdd( ::aDelLines, nil )
      endif
      AIns( ::aDelLines, 1 )
      ::aDelLines[ 1 ] = Str( Cont ) + "$" + ;
                         Str( nLine ) + "#" + ;
                         cOper + "&" + ::aLines[ nLine ]
   endif

return nil

//----------------------------------------------------------------------------//
/*
METHOD AddRedo( nLine, cOper, Cont )  CLASS TTxtEdit

   DEFAULT Cont := 1

      if Len( ::aRDelLines ) < ::nDelLines
         AAdd( ::aRDelLines, nil )
      endif
      AIns( ::aRDelLines, 1 )
      ::aRDelLines[ 1 ] = Str( Cont ) + "$" + ;
                         Str( nLine ) + "#" + ;
                         cOper + "&" + ::aLines[ nLine ]

return nil
*/
//----------------------------------------------------------------------------//

METHOD MouseMove( nRow, nCol, nKeyFlags ) CLASS TTxtEdit

   local nLine := Int( nRow / ::nChrHeight )

   if ::lDrag
      return Super:MouseMove( nRow, nCol, nKeyFlags )
   else
      if Len( ::aLines ) == 0
         return Super:MouseMove( nRow, nCol, nKeyFlags )
      endif
      if nLine >= Len( ::aLines )
         return nil
      endif
      if ::lMPressed
         if ::nBlkEndRow != 0
            ::nBlkEndRow = ::nLineTop + nLine
         else
            ::nBlkStartRow = ::nLineTop + nLine
            ::nBlkEndRow   = ::nLineTop + nLine
         endif
         ::nLineRow = nLine
         ::nRow     = nLine * ::nChrHeight
         ::nCol     = nCol
         SetCaretPos( ::nCol, ::nRow )
         ::Refresh()
      endif
   endif

return Super:MouseMove( nRow, nCol, nKeyFlags )

//----------------------------------------------------------------------------//

METHOD PageUp() CLASS TTxtEdit

   If ( ::nLineTop - ::nVisLines ) > 0
      ::nLineTop -= ::nVisLines
      ::Refresh()
      ::oVScroll:SetPos( ::nLine() )
   else
      ::GoTop()
   endif

return nil

//----------------------------------------------------------------------------//

METHOD PageDown() CLASS TTxtEdit

  If ( ::nLineTop + ::nVisLines ) < Len( ::aLines )
     ::nLineTop += ::nVisLines
     ::Refresh()
     ::oVScroll:SetPos( ::nLine() )
  else
     ::GoBottom()
  endif

return nil

//----------------------------------------------------------------------------//